%%% Example

%info=trainTest(a.fea, a.gnd, b.fea, b.gnd, 'algorithm', 'bppNMFSVM', 'k', 5:5:100,'C',0.1,'tol',1e-4,'output','../result/cbcl_class_nmf');

% fea, gnd: data & labels (each row is a sample)
% varargin:
%   .algorithm
%   .loocv       : true/false
%   .fold        : k-fold cross-validation
%   other parameters for the algorithm
% info: struct array
%   each field is a parameter setting
%   .accuracy: accuracy rate 
function info = trainTest(Xtrain, ytrain, Xtest, ytest, varargin)
    par.algorithm = 'linearSVM';
    par.output = '';
    par.add = 1;
    par = process_parameter(par, varargin{:});
    
    info = [];
    if par.add && exist(par.output, 'file') 
        load(par.output);
    end
    x = varargin;
    build(1,struct);  % build all sets of parameter
    
    function build(i, s)
    % complete a set of parameter
        if i > length(x) || i+1 > length(x)
            f = fieldnames(s);
            arg(1:2:length(f)*2) = f;
            arg(2:2:length(f)*2) = struct2cell(s);
            command_str = [par.algorithm '(Xtrain, ytrain, Xtest, ytest, arg{:})'];
            display(['algorithm = ' par.algorithm]);
            display(arg);
            s.accuracy = eval(command_str);
            display(['acc=' num2str(s.accuracy)]);

            info = [info s];
            if ~isempty(par.output)
                save(par.output, 'info');
            end
            return;
        end
        
        if iscell(x{i+1})
            for j = 1:length(x{i+1})
                s.(x{i}) = x{i+1}{j};
                build(i+2,s);
            end
        elseif isstr(x{i+1})
            s.(x{i}) = x{i+1};
            build(i+2,s);
        else % array
            for j = 1:length(x{i+1})
                s.(x{i}) = x{i+1}(j);
                build(i+2,s);
            end            
        end
    end
end

function acc = linearSVM(Xtrain, ytrain, Xtest, ytest, varargin) 
    par.c = 1;
    par = process_parameter(par, varargin{:});
    svm = train(ytrain, sparse(Xtrain), ['-q -c ' num2str(par.c)]);
    [label, acc] = predict(ytest, sparse(Xtest), svm, '-q');
    acc = acc(1);
end

function acc = multiplicativeNMFSVM(fea, gnd, varargin) 
    par.k = 5;
    par.fold = 10;
    par.c = 1;
    par = process_parameter(par, varargin{:});

    [W,H] = multiplicativeNMF(fea,  varargin{:}, 'k', par.k, 'basis', 'H');
    acc = train(gnd, sparse(W), ['-q -v ' num2str(par.fold) ...
                           ' -c ' num2str(par.c)]);
end

function acc = bppNMFSVM(Xtrain, ytrain, Xtest, ytest, varargin) 
    par.k = 5;
    par.c = 1;
    par = process_parameter(par, varargin{:});

    [W,H] = nmf(Xtrain, par.k, varargin{:});
    [W, H] = normalizeNMFFactor(W,H','H');
        
    svm = train(ytrain, sparse(W), ['-q -c ' num2str(par.c)]);
    Wtest = nnlsm_blockpivot(H, Xtest', false)';
    [label, acc] = predict(ytest, sparse(Wtest), svm, '-q');
    acc = acc(1);
end

function acc = svdSVM(Xtrain, ytrain, Xtest, ytest, varargin) 
    par.k = 5;
    par.c = 1;
    par = process_parameter(par, varargin{:});

    [U,S,V] = svds(Xtrain, par.k);

    svm = train(ytrain, sparse(U*S), ['-q -c ' num2str(par.c)]);
    [label, acc] = predict(ytest, sparse(Xtest*V), svm, '-q');
    acc = acc(1);
end
